/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "s_sound.h"
#include "a_action.h"
#include "m_random.h"
#include "a_hexenglobal.h"
#include "i_system.h"
#include "thingdef/thingdef.h"
#include "g_level.h"
*/

//============================================================================
//
//	Sorcerer stuff
//
// Sorcerer Variables
//		specialf1		Angle of ball 1 (all others relative to that)
//		StopBall		which ball to stop at in stop mode (MT_???)
//		args[0]			Defense time
//		args[1]			Number of full rotations since stopping mode
//		args[2]			Target orbit speed for acceleration/deceleration
//		args[3]			Movement mode (see SORC_ macros)
//		args[4]			Current ball orbit speed
//	Sorcerer Ball Variables
//		specialf1		Previous angle of ball (for woosh)
//		special2		Countdown of rapid fire (FX4)
//============================================================================

#define SORCBALL_INITIAL_SPEED 		7
#define SORCBALL_TERMINAL_SPEED		25
#define SORCBALL_SPEED_ROTATIONS 	5
#define SORC_DEFENSE_TIME			255
#define SORC_DEFENSE_HEIGHT			45
#define BOUNCE_TIME_UNIT			(35/2)
#define SORCFX4_RAPIDFIRE_TIME		(6*3)		// 3 seconds
#define SORCFX4_SPREAD_ANGLE		20

#define SORC_DECELERATE		0
#define SORC_ACCELERATE 	1
#define SORC_STOPPING		2
#define SORC_FIRESPELL		3
#define SORC_STOPPED		4
#define SORC_NORMAL			5
#define SORC_FIRING_SPELL	6

#define BALL1_ANGLEOFFSET	0.
#define BALL2_ANGLEOFFSET	120.
#define BALL3_ANGLEOFFSET	240.

void A_SlowBalls (AActor *actor);
void A_StopBalls (AActor *actor);
void A_AccelBalls (AActor *actor);
void A_DecelBalls (AActor *actor);
void A_SorcOffense2 (AActor *actor);
void A_DoBounceCheck (AActor *actor, const char *sound);

static FRandom pr_heresiarch ("Heresiarch");

// The Heresiarch him/itself ------------------------------------------------

class AHeresiarch : public AActor
{
	DECLARE_CLASS (AHeresiarch, AActor)
public:
	const PClass *StopBall;
	DAngle BallAngle;

	void Serialize (FArchive &arc);
	void Die (AActor *source, AActor *inflictor, int dmgflags);
};

IMPLEMENT_CLASS (AHeresiarch)

void AHeresiarch::Serialize (FArchive &arc)
{
	Super::Serialize (arc);
	arc << StopBall << BallAngle;
}

void AHeresiarch::Die (AActor *source, AActor *inflictor, int dmgflags)
{
	// The heresiarch just executes a script instead of a special upon death
	int script = special;
	special = 0;

	Super::Die (source, inflictor, dmgflags);

	if (script != 0)
	{
		P_StartScript (this, NULL, script, level.MapName, NULL, 0, 0);
	}
}

// Base class for the balls flying around the Heresiarch's head -------------

class ASorcBall : public AActor
{
	DECLARE_CLASS (ASorcBall, AActor)
public:
	virtual void DoFireSpell ();
	virtual void SorcUpdateBallAngle ();
	virtual void CastSorcererSpell ();
	DAngle AngleOffset;
	DAngle OldAngle;

	void Serialize (FArchive &arc)
	{
		Super::Serialize (arc);
		arc << AngleOffset << OldAngle;
	}

	bool SpecialBlastHandling (AActor *source, double strength)
	{ // don't blast sorcerer balls
		return false;
	}
};

IMPLEMENT_CLASS (ASorcBall)

// First ball (purple) - fires projectiles ----------------------------------

class ASorcBall1 : public ASorcBall
{
	DECLARE_CLASS (ASorcBall1, ASorcBall)
public:
	void BeginPlay ()
	{
		Super::BeginPlay ();
		AngleOffset = BALL1_ANGLEOFFSET;
	}
	virtual void DoFireSpell ();
	virtual void SorcUpdateBallAngle ();
	virtual void CastSorcererSpell ();
};

IMPLEMENT_CLASS (ASorcBall1)

// Second ball (blue) - generates the shield --------------------------------

class ASorcBall2 : public ASorcBall
{
	DECLARE_CLASS (ASorcBall2, ASorcBall)
public:
	void BeginPlay ()
	{
		Super::BeginPlay ();
		AngleOffset = BALL2_ANGLEOFFSET;
	}
	virtual void CastSorcererSpell ();
};

IMPLEMENT_CLASS (ASorcBall2)

// Third ball (green) - summons Bishops -------------------------------------

class ASorcBall3 : public ASorcBall
{
	DECLARE_CLASS (ASorcBall3, ASorcBall)
public:
	void BeginPlay ()
	{
		Super::BeginPlay ();
		AngleOffset = BALL3_ANGLEOFFSET;
	}
	virtual void CastSorcererSpell ();
};

IMPLEMENT_CLASS (ASorcBall3)

// Sorcerer spell 1 (The burning, bouncing head thing) ----------------------

//============================================================================
//
// SorcBall::DoFireSpell
//
//============================================================================

void ASorcBall::DoFireSpell ()
{
	CastSorcererSpell ();

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	target->args[3] = SORC_STOPPED;

	// [BB] If we're the server, tell the clients to set the arguments of target.
	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		SERVERCOMMANDS_SetThingArguments( target );

}

//============================================================================
//
// SorcBall1::DoFireSpell
//
//============================================================================

void ASorcBall1::DoFireSpell ()
{
	// [BB] This is server-side (involves random numbers).
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	if (pr_heresiarch() < 200)
	{
		S_Sound (target, CHAN_VOICE, "SorcererSpellCast", 1, ATTN_NONE, true);	// [BB] Inform the clients.
		special2 = SORCFX4_RAPIDFIRE_TIME;
		args[4] = 128;
		target->args[3] = SORC_FIRING_SPELL;

		// [BB] If we're the server, tell the clients to set special2 and arguments.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SetThingSpecial2( this );
			SERVERCOMMANDS_SetThingArguments( this );
			SERVERCOMMANDS_SetThingArguments( target );
		}
	}
	else
	{
		Super::DoFireSpell ();
	}
}

//============================================================================
//
// A_SorcSpinBalls
//
// Spawn spinning balls above head - actor is sorcerer
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcSpinBalls)
{
	PARAM_ACTION_PROLOGUE_TYPE(AHeresiarch);

	AActor *mo;

	self->SpawnState += 2;		// [RH] Don't spawn balls again
	A_SlowBalls(self);
	self->args[0] = 0;								// Currently no defense
	self->args[3] = SORC_NORMAL;
	self->args[4] = SORCBALL_INITIAL_SPEED;		// Initial orbit speed
	self->BallAngle = 1.;

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return 0;
	}

	DVector3 pos = self->PosPlusZ(-self->Floorclip + self->Height);
	
	mo = Spawn("SorcBall1", pos, NO_REPLACE);
	if (mo)
	{
		mo->target = self;
		mo->special2 = SORCFX4_RAPIDFIRE_TIME;

		// [BB] If we're the server, tell the clients to spawn the thing and set its target and special2.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SpawnThing( mo );
			SERVERCOMMANDS_SetThingTarget( mo );
			SERVERCOMMANDS_SetThingSpecial2( mo );
		}
	}
	mo = Spawn("SorcBall2", pos, NO_REPLACE);
	if (mo) mo->target = self;

	// [BB] If we're the server, tell the clients to spawn the thing and set its target.
	if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
	{
		SERVERCOMMANDS_SpawnThing( mo );
		SERVERCOMMANDS_SetThingTarget( mo );
	}

	mo = Spawn("SorcBall3", pos, NO_REPLACE);
	if (mo) mo->target = self;

	// [BB] If we're the server, tell the clients to spawn the thing and set its target.
	if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
	{
		SERVERCOMMANDS_SpawnThing( mo );
		SERVERCOMMANDS_SetThingTarget( mo );
	}

	return 0;
}


//============================================================================
//
// A_SorcBallOrbit
//
// - actor is ball
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcBallOrbit)
{
	PARAM_ACTION_PROLOGUE_TYPE(ASorcBall);

	// [BB] The client should do most of this stuff here. This way the balls
	// can smoothly orbit the Heresiarch without causing too much net traffic.

	// [RH] If no parent, then die instead of crashing
	if (self->target == NULL)
	{
		// [BB] Tell clients to set the thing's state.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			SERVERCOMMANDS_SetThingState( self, STATE_PAIN );

		self->SetState (self->FindState(NAME_Pain));
		return 0;
	}

	int mode = self->target->args[3];
	AHeresiarch *parent = barrier_cast<AHeresiarch *>(self->target);
	double dist = parent->radius - (self->radius*2);

#if 0
	// This cannot happen anymore because this is defined locally in SorcBall
	if (!self->IsKindOf (RUNTIME_CLASS(ASorcBall)))
	{
		I_Error ("Corrupted sorcerer:\nTried to use a %s", self->GetClass()->TypeName.GetChars());
	}
#endif

	if (self->target->health <= 0)
	{
		// [BB] Tell clients to set the thing's state.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			SERVERCOMMANDS_SetThingState( self, STATE_PAIN );

		self->SetState (self->FindState(NAME_Pain));
		return 0;
	}

	DAngle prevangle = self->OldAngle;
	DAngle baseangle = parent->BallAngle;
	DAngle angle = baseangle + self->AngleOffset;

	self->Angles.Yaw = angle;

	switch (mode)
	{
	case SORC_NORMAL:			// Balls rotating normally
		self->SorcUpdateBallAngle ();
		break;

	case SORC_DECELERATE:		// Balls decelerating
		A_DecelBalls(self);
		self->SorcUpdateBallAngle ();
		break;

	case SORC_ACCELERATE:		// Balls accelerating
		A_AccelBalls(self);
		self->SorcUpdateBallAngle ();
		break;

	case SORC_STOPPING:			// Balls stopping
		if ((parent->StopBall == self->GetClass()) &&
			 (parent->args[1] > SORCBALL_SPEED_ROTATIONS) &&
			 absangle(angle, parent->Angles.Yaw) < 42.1875)
		{
			// [BB] This is server-side (since StopBall involved random numbers).
			if ( NETWORK_InClientMode() )
			{
				break;
			}

			// Can stop now
			self->target->args[3] = SORC_FIRESPELL;
			self->target->args[4] = 0;
			// Set angle so self angle == sorcerer angle
			parent->BallAngle = parent->Angles.Yaw - self->AngleOffset;

			// [BB] If we're the server, tell the clients to set the arguments of actor->target and special1 of parent.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			{
				SERVERCOMMANDS_SetThingArguments( self->target );
				// [BB] FIXME: Change SERVERCOMMANDS_SetThingSpecial1 to set BallAngle.
				SERVERCOMMANDS_SetThingSpecial1( parent );
			}
		}
		else
		{
			self->SorcUpdateBallAngle ();
		}
		break;

	case SORC_FIRESPELL:			// Casting spell
		if (parent->StopBall == self->GetClass())
		{
			// [BB] This is server-side (since StopBall involved random numbers).
			if ( NETWORK_InClientMode() )
			{
				break;
			}

			// Put sorcerer into special throw spell anim
			if (parent->health > 0)
			{
				// [BB] Tell clients to change this thing's state.
				if ( NETWORK_GetState( ) == NETSTATE_SERVER )
					SERVERCOMMANDS_SetThingFrame( parent, parent->FindState("Attack1") );

				parent->SetState (parent->FindState("Attack1"));
			}

			self->DoFireSpell ();
		}
		break;

	case SORC_FIRING_SPELL:
		if (parent->StopBall == self->GetClass())
		{
			// [BB] This is server-side (since StopBall involved random numbers).
			if ( NETWORK_InClientMode() )
			{
				break;
			}

			if (self->special2-- <= 0)
			{
				// Done rapid firing 
				parent->args[3] = SORC_STOPPED;

				// [BB] If we're the server, tell the clients to set the arguments of parent.
				if ( NETWORK_GetState( ) == NETSTATE_SERVER )
					SERVERCOMMANDS_SetThingArguments( parent );

				// Back to orbit balls
				if (parent->health > 0)
				{
					// [BB] Tell clients to change this thing's state.
					if ( NETWORK_GetState( ) == NETSTATE_SERVER )
						SERVERCOMMANDS_SetThingFrame( parent, parent->FindState("Attack2") );

					parent->SetState (parent->FindState("Attack2"));
				}
			}
			else
			{
				// Do rapid fire spell
				A_SorcOffense2(self);
			}
		}
		break;

	case SORC_STOPPED:			// Balls stopped
	default:
		break;
	}

	if ( angle.BAMs() < prevangle.BAMs() && (parent->args[4]==SORCBALL_TERMINAL_SPEED))
	{
		parent->args[1]++;			// Bump rotation counter
		// Completed full rotation - make woosh sound
		S_Sound (self, CHAN_BODY, "SorcererBallWoosh", 1, ATTN_NORM);
	}
	self->OldAngle = angle;		// Set previous angle

	DVector3 pos = parent->Vec3Angle(dist, angle, -parent->Floorclip + parent->Height);
	self->SetOrigin (pos, true);
	self->floorz = parent->floorz;
	self->ceilingz = parent->ceilingz;
	return 0;
}

//============================================================================
//
// A_SpeedBalls
//
// Set balls to speed mode - self is sorcerer
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SpeedBalls)
{
	PARAM_ACTION_PROLOGUE;

	self->args[3] = SORC_ACCELERATE;				// speed mode
	self->args[2] = SORCBALL_TERMINAL_SPEED;		// target speed
	return 0;
}


//============================================================================
//
// A_SlowBalls
//
// Set balls to slow mode - actor is sorcerer
//
//============================================================================

void A_SlowBalls(AActor *self)
{
	self->args[3] = SORC_DECELERATE;				// slow mode
	self->args[2] = SORCBALL_INITIAL_SPEED;		// target speed
}

//============================================================================
//
// A_StopBalls
//
// Instant stop when rotation gets to ball in special2
//		self is sorcerer
//
//============================================================================

void A_StopBalls(AActor *scary)
{
	AHeresiarch *self = static_cast<AHeresiarch *> (scary);
	int chance = pr_heresiarch();
	self->args[3] = SORC_STOPPING;				// stopping mode
	self->args[1] = 0;							// Reset rotation counter

	if ((self->args[0] <= 0) && (chance < 200))
	{
		self->StopBall = RUNTIME_CLASS(ASorcBall2);	// Blue
	}
	else if((self->health < (self->SpawnHealth() >> 1)) &&
			(chance < 200))
	{
		self->StopBall = RUNTIME_CLASS(ASorcBall3);	// Green
	}
	else
	{
		self->StopBall = RUNTIME_CLASS(ASorcBall1);	// Yellow
	}
}

//============================================================================
//
// A_AccelBalls
//
// Increase ball orbit speed - actor is ball
//
//============================================================================

void A_AccelBalls(AActor *self)
{
	AActor *sorc = self->target;

	if (sorc->args[4] < sorc->args[2])
	{
		sorc->args[4]++;
	}
	else
	{
		sorc->args[3] = SORC_NORMAL;
		if (sorc->args[4] >= SORCBALL_TERMINAL_SPEED)
		{
			// Reached terminal velocity - stop balls
			A_StopBalls(sorc);
		}
	}
}

//============================================================================
//
// A_DecelBalls
//
// Decrease ball orbit speed - actor is ball
//
//============================================================================

void A_DecelBalls(AActor *self)
{
	AActor *sorc = self->target;

	if (sorc->args[4] > sorc->args[2])
	{
		sorc->args[4]--;
	}
	else
	{
		sorc->args[3] = SORC_NORMAL;
	}
}

//============================================================================
//
// ASorcBall1::SorcUpdateBallAngle
//
// Update angle if first ball
//============================================================================

void ASorcBall1::SorcUpdateBallAngle ()
{
	barrier_cast<AHeresiarch*>(target)->BallAngle += target->args[4];
}

//============================================================================
//
// ASorcBall::SorcUpdateBallAngle
//
//============================================================================

void ASorcBall::SorcUpdateBallAngle ()
{
}

//============================================================================
//
// ASorcBall::CastSorcererSpell
//
// Make noise and change the parent sorcerer's animation
//
//============================================================================

void ASorcBall::CastSorcererSpell ()
{
	S_Sound (target, CHAN_VOICE, "SorcererSpellCast", 1, ATTN_NONE, true);	// [BB] Inform the clients.

	// Put sorcerer into throw spell animation
	if (target->health > 0)
	{
		// [BB] Tell clients to change this thing's state.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			SERVERCOMMANDS_SetThingFrame( target, target->FindState("Attack2") );

		target->SetState (target->FindState("Attack2"));
	}
}

//============================================================================
//
// ASorcBall2::CastSorcererSpell
//
// Defensive
//
//============================================================================

void ASorcBall2::CastSorcererSpell ()
{
	Super::CastSorcererSpell ();

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	AActor *parent = target;
	AActor *mo;

	mo = Spawn("SorcFX2", PosPlusZ(parent->Floorclip + SORC_DEFENSE_HEIGHT), ALLOW_REPLACE);
	parent->flags2 |= MF2_REFLECTIVE|MF2_INVULNERABLE;
	parent->args[0] = SORC_DEFENSE_TIME;
	if (mo) mo->target = parent;

	// [BB] If we're the server, tell the clients to set some things and spawn mo.
	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
	{
		SERVERCOMMANDS_SetThingFlags( parent, FLAGSET_FLAGS2 );
		SERVERCOMMANDS_SetThingArguments( parent );
		if ( mo )
		{
			SERVERCOMMANDS_SpawnThing( mo );
			SERVERCOMMANDS_SetThingTarget( mo );
		}
	}
}

//============================================================================
//
// ASorcBall3::CastSorcererSpell
//
// Reinforcements
//
//============================================================================

void ASorcBall3::CastSorcererSpell ()
{
	Super::CastSorcererSpell ();

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	AActor *mo;
	DAngle ang1, ang2;
	AActor *parent = target;

	ang1 = Angles.Yaw.Degrees - 45;
	ang2 = Angles.Yaw.Degrees + 45;
	PClassActor *cls = PClass::FindActor("SorcFX3");
	if (health < (SpawnHealth()/3))
	{	// Spawn 2 at a time
		mo = P_SpawnMissileAngle(parent, cls, ang1, 4.);
		if (mo) mo->target = parent;

		// [BB] If we're the server, tell the clients to spawn the missile.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
			SERVERCOMMANDS_SpawnMissile( mo );

		mo = P_SpawnMissileAngle(parent, cls, ang2, 4.);
		if (mo) mo->target = parent;

		// [BB] If we're the server, tell the clients to spawn the missile.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
			SERVERCOMMANDS_SpawnMissile( mo );
	}			
	else
	{
		if (pr_heresiarch() < 128)
			ang1 = ang2;
		mo = P_SpawnMissileAngle(parent, cls, ang1, 4.);
		if (mo) mo->target = parent;

		// [BB] If we're the server, tell the clients to spawn the missile.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
			SERVERCOMMANDS_SpawnMissile( mo );
	}
}


/*
void A_SpawnReinforcements(AActor *actor)
{
	AActor *parent = self->target;
	AActor *mo;
	DAngle ang;

	ang = P_Random();
	mo = P_SpawnMissileAngle(actor, MT_SORCFX3, ang, 5.);
	if (mo) mo->target = parent;
}
*/

//============================================================================
//
// SorcBall1::CastSorcererSpell
//
// Offensive
//
//============================================================================

void ASorcBall1::CastSorcererSpell ()
{
	Super::CastSorcererSpell ();

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	AActor *mo;
	DAngle ang1, ang2;
	AActor *parent = target;

	ang1 = Angles.Yaw.Degrees + 70;
	ang2 = Angles.Yaw.Degrees - 70;
	PClassActor *cls = PClass::FindActor("SorcFX1");
	mo = P_SpawnMissileAngle (parent, cls, ang1, 0);
	if (mo)
	{
		mo->target = parent;
		mo->tracer = parent->target;
		mo->args[4] = BOUNCE_TIME_UNIT;
		mo->args[3] = 15;				// Bounce time in seconds

		// [BB] If we're the server, tell the clients to spawn the missile and set the arguments.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
		{
			SERVERCOMMANDS_SpawnMissile( mo );
			SERVERCOMMANDS_SetThingArguments( mo );
		}
	}
	mo = P_SpawnMissileAngle (parent, cls, ang2, 0);
	if (mo)
	{
		mo->target = parent;
		mo->tracer = parent->target;
		mo->args[4] = BOUNCE_TIME_UNIT;
		mo->args[3] = 15;				// Bounce time in seconds

		// [BB] If we're the server, tell the clients to spawn the missile and set the arguments.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
		{
			SERVERCOMMANDS_SpawnMissile( mo );
			SERVERCOMMANDS_SetThingArguments( mo );
		}
	}
}

//============================================================================
//
// A_SorcOffense2
//
// Actor is ball
//
//============================================================================

void A_SorcOffense2(AActor *self)
{
	DAngle ang1;
	AActor *mo;
	double delta;
	int index;
	AActor *parent = self->target;
	AActor *dest = parent->target;
	double dist;

	// [RH] If no enemy, then don't try to shoot.
	if (dest == NULL)
	{
		return;
	}

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	index = self->args[4];
	self->args[4] = (self->args[4] + 15) & 255;
	delta = DAngle(index * (360 / 256.f)).Sin() * SORCFX4_SPREAD_ANGLE;

	ang1 = self->Angles.Yaw + delta;

	// [BB] If we're the server, tell the clients to set the arguments of actor.
	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		SERVERCOMMANDS_SetThingArguments( self );

	mo = P_SpawnMissileAngle(parent, PClass::FindActor("SorcFX4"), ang1, 0);
	if (mo)
	{
		mo->special2 = 35*5/2;		// 5 seconds
		dist = mo->DistanceBySpeed(dest, mo->Speed);
		mo->Vel.Z = (dest->Z() - mo->Z()) / dist;

		// [BB] If we're the server, tell the clients to spawn the missile and set its special2.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SpawnMissile( mo );
			SERVERCOMMANDS_SetThingSpecial2( mo );
		}
	}
}

//============================================================================
//
// A_SorcBossAttack
//
// Resume ball spinning
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcBossAttack)
{
	PARAM_ACTION_PROLOGUE;

	self->args[3] = SORC_ACCELERATE;
	self->args[2] = SORCBALL_INITIAL_SPEED;
	return 0;
}

//============================================================================
//
// A_SpawnFizzle
//
// spell cast magic fizzle
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SpawnFizzle)
{
	PARAM_ACTION_PROLOGUE;
	int speed = (int)self->Speed;
	DAngle rangle;
	AActor *mo;
	int ix;

	DVector3 pos = self->Vec3Angle(5., self->Angles.Yaw, -self->Floorclip + self->Height / 2. );
	for (ix=0; ix<5; ix++)
	{
		mo = Spawn("SorcSpark1", pos, ALLOW_REPLACE);
		if (mo)
		{
			rangle = self->Angles.Yaw + (pr_heresiarch() % 5) * (4096 / 360.);
			mo->Vel.X = (pr_heresiarch() % speed) * rangle.Cos();
			mo->Vel.Y = (pr_heresiarch() % speed) * rangle.Sin();
			mo->Vel.Z = 2;
		}
	}
	return 0;
}


//============================================================================
//
// A_SorcFX1Seek
//
// Yellow spell - offense
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcFX1Seek)
{
	PARAM_ACTION_PROLOGUE;

	A_DoBounceCheck (self, "SorcererHeadScream");
	P_SeekerMissile(self, 2, 6);
	return 0;
}


//============================================================================
//
// A_SorcFX2Split
//
// Blue spell - defense
//
//============================================================================
//
// FX2 Variables
//		specialf1		current angle
//		special2
//		args[0]		0 = CW,  1 = CCW
//		args[1]		
//============================================================================

// Split ball in two
DEFINE_ACTION_FUNCTION(AActor, A_SorcFX2Split)
{
	PARAM_ACTION_PROLOGUE;

	AActor *mo;

	// [BB] This is server-side. The client only destroy the actor.
	if ( NETWORK_InClientMode() )
	{
		self->Destroy ();
		return 0;
	}

	mo = Spawn(self->GetClass(), self->Pos(), NO_REPLACE);
	if (mo)
	{
		mo->target = self->target;
		mo->args[0] = 0;									// CW
		mo->specialf1 = self->Angles.Yaw.Degrees;			// Set angle

		// [BB] If we're the server, tell the clients to spawn the thing.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SpawnThing( mo );
			SERVERCOMMANDS_SetThingTarget( mo );
			SERVERCOMMANDS_SetThingArguments( mo );
			SERVERCOMMANDS_SetThingSpecial1( mo );
			SERVERCOMMANDS_SetThingFrame( mo, mo->FindState("Orbit") );
		}

		mo->SetState (mo->FindState("Orbit"));
	}
	mo = Spawn(self->GetClass(), self->Pos(), NO_REPLACE);
	if (mo)
	{
		mo->target = self->target;
		mo->args[0] = 1;									// CCW
		mo->specialf1 = self->Angles.Yaw.Degrees;			// Set angle

		// [BB] If we're the server, tell the clients to spawn the thing.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SpawnThing( mo );
			SERVERCOMMANDS_SetThingTarget( mo );
			SERVERCOMMANDS_SetThingArguments( mo );
			SERVERCOMMANDS_SetThingSpecial1( mo );
			SERVERCOMMANDS_SetThingFrame( mo, mo->FindState("Orbit") );
		}

		mo->SetState (mo->FindState("Orbit"));
	}
	self->Destroy ();
	return 0;
}

//============================================================================
//
// A_SorcFX2Orbit
//
// Orbit FX2 about sorcerer
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcFX2Orbit)
{
	PARAM_ACTION_PROLOGUE;

	DAngle angle;
	DVector3 pos;
	AActor *parent = self->target;

	// [RH] If no parent, then disappear
	if (parent == NULL)
	{
		self->Destroy();
		return 0;
	}

	double dist = parent->radius;

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() == false )
	{
		if ((parent->health <= 0) ||		// Sorcerer is dead
			(!parent->args[0]))				// Time expired
		{
			// [BB] Tell clients to set the thing's state.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
				SERVERCOMMANDS_SetThingState( self, STATE_DEATH );

			self->SetState (self->FindState(NAME_Death));
			parent->args[0] = 0;
			parent->flags2 &= ~MF2_REFLECTIVE;
			parent->flags2 &= ~MF2_INVULNERABLE;

			// [BB] If we're the server, tell the clients to update flags and arguments.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			{
				SERVERCOMMANDS_SetThingFlags( parent, FLAGSET_FLAGS2 );
				SERVERCOMMANDS_SetThingArguments( parent );
			}
		}

		if (self->args[0] && (parent->args[0]-- <= 0))		// Time expired
		{
			// [BB] Tell clients to set the thing's state.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
				SERVERCOMMANDS_SetThingState( self, STATE_DEATH );

			self->SetState (self->FindState(NAME_Death));
			parent->args[0] = 0;
			parent->flags2 &= ~MF2_REFLECTIVE;

			// [BB] If we're the server, tell the clients to update flags and arguments.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			{
				SERVERCOMMANDS_SetThingFlags( parent, FLAGSET_FLAGS2 );
				SERVERCOMMANDS_SetThingArguments( parent );
			}
		}
	}

	// Move to new position based on angle
	if (self->args[0])		// Counter clock-wise
	{
		self->specialf1 += 10;
		angle = self->specialf1;
		pos = parent->Vec3Angle(dist, angle, parent->Floorclip + SORC_DEFENSE_HEIGHT);
		pos.Z += 15 * angle.Cos();
		// Spawn trailer
		Spawn("SorcFX2T1", pos, ALLOW_REPLACE);
	}
	else							// Clock wise
	{
		self->specialf1 -= 10;
		angle = self->specialf1;
		pos = parent->Vec3Angle(dist, angle, parent->Floorclip + SORC_DEFENSE_HEIGHT);
		pos.Z += 20 * angle.Sin();
		// Spawn trailer
		Spawn("SorcFX2T1", pos, ALLOW_REPLACE);
	}

	self->SetOrigin (pos, true);
	self->floorz = parent->floorz;
	self->ceilingz = parent->ceilingz;
	return 0;
}

//============================================================================
//
// A_SpawnBishop
//
// Green spell - spawn bishops
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SpawnBishop)
{
	PARAM_ACTION_PROLOGUE;

	// [BB] This is server-side. The client only destroy the actor.
	if ( NETWORK_InClientMode() )
	{
		self->Destroy ();
		return 0;
	}

	AActor *mo;
	mo = Spawn("Bishop", self->Pos(), ALLOW_REPLACE);
	if (mo)
	{
		if (!P_TestMobjLocation(mo))
		{
			mo->ClearCounters();
			mo->Destroy ();
		}
		else if (self->target != NULL)
		{ // [RH] Make the new bishops inherit the Heriarch's target
			mo->CopyFriendliness (self->target, true);
			mo->master = self->target;

			// [BB] If we're the server, tell the clients to spawn the thing.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
				SERVERCOMMANDS_SpawnThing( mo );
		}
	}
	self->Destroy ();
	return 0;
}

//============================================================================
//
// A_SorcererBishopEntry
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcererBishopEntry)
{
	PARAM_ACTION_PROLOGUE;

	Spawn("SorcFX3Explosion", self->Pos(), ALLOW_REPLACE);
	S_Sound (self, CHAN_VOICE, self->SeeSound, 1, ATTN_NORM);
	return 0;
}

//============================================================================
//
// A_SorcFX4Check
//
// FX4 - rapid fire balls
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcFX4Check)
{
	PARAM_ACTION_PROLOGUE;

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return 0;
	}

	if (self->special2-- <= 0)
	{
		// [BB] Tell clients to set the thing's state.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			SERVERCOMMANDS_SetThingState( self, STATE_DEATH );

		self->SetState (self->FindState(NAME_Death));
	}
	return 0;
}

//============================================================================
//
// A_SorcBallPop
//
// Ball death - bounce away in a random direction
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcBallPop)
{
	PARAM_ACTION_PROLOGUE;

	S_Sound (self, CHAN_BODY, "SorcererBallPop", 1, ATTN_NONE);
	self->flags &= ~MF_NOGRAVITY;
	self->Gravity = 1. / 8;;

	self->Vel.X = ((pr_heresiarch()%10)-5);
	self->Vel.Y = ((pr_heresiarch()%10)-5);
	self->Vel.Z = (2+(pr_heresiarch()%3));
	self->args[4] = BOUNCE_TIME_UNIT;	// Bounce time unit
	self->args[3] = 5;					// Bounce time in seconds
	return 0;
}

//============================================================================
//
// A_DoBounceCheck
//
//============================================================================

void A_DoBounceCheck (AActor *self, const char *sound)
{
	if (self->args[4]-- <= 0)
	{
		if (self->args[3]-- <= 0)
		{
			self->SetState (self->FindState(NAME_Death));
			S_Sound (self, CHAN_BODY, sound, 1, ATTN_NONE);
		}
		else
		{
			self->args[4] = BOUNCE_TIME_UNIT;
		}
	}
}

//============================================================================
//
// A_BounceCheck
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_BounceCheck)
{
	PARAM_ACTION_PROLOGUE;

	A_DoBounceCheck (self, "SorcererBigBallExplode");
	return 0;
}
